#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <iostream>

const int VERSION = 1;

class Network {
 public: 
  Network(const char *address, int port) 
      : address_(address), port_(port) {}

  ~Network() {
    disconnect();
  }

  int establish_connection(char *address = NULL, int port = 0) {
    if (address != NULL) address_ = address;
    if (port != 0) port_ = port;
    if ((sock_ = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
      perror("socket");
      return errno;
    }
    bzero(&addr_, sizeof(addr_));
    addr_.sin_family = AF_INET;
    addr_.sin_port = htons(port_);
    inet_aton(address_, &addr_.sin_addr);
    if (connect(sock_, (struct sockaddr *)&addr_, 
                sizeof(addr_)) < 0) {
      perror("connect");
      return errno;
    }
    return 0;
  }

  int disconnect() {
    close(sock_);
  }

  int send_bytes(char *message) {
    int total = 0;
    int n;
    int len = strlen(message);
    while (total < len) {
      n = send(sock_, message + total, len - total, 0);
      if (n == -1)
        break;
      total += n;
    }
    return (n == -1 ? -1 : total);
  }
  
  int receive_bytes(char *message) {
    int total = 0;
    int n = 0;
    int len = 1024;
    while (n != -1) {
      n = recv(sock_, message + total, len, 0);
      if (n == -1)
        break;
      total += n;
    }
    return (n == -1 ? -1 : total);
  }

 private:
  sockaddr_in addr_;
  const char *address_;
  int port_;
  int sock_;
};

class Sender {
 public:
  explicit Sender(const std::string& name, const char* address = NULL, 
                  int port = 0) 
      : name_(name) {
    net_ = new Network(address, port); 
  }

  ~Sender() {
    delete net_;
  }

  const std::string& name() const {
    return name_;
  }

  int connect(char* address = NULL, int port = 0) {
    return net_->establish_connection(address, port);
  }

  void send_message(const std::string& message) {
    char buf[1024];
    sprintf(buf, "send%s: %s\n", name_.c_str(), message.c_str());
    net_->send_bytes(buf);
  }

  std::string get_all_from_server(int N) {
    char request[1024];
    sprintf(request, "get%d\n", N);
    net_->send_bytes(request);
    char message[1024];
    net_->receive_bytes(message);
    std::cerr << "&&& " << message;
    return std::string(message);
  }

  void greet_server() {
    char buf[1024];
    sprintf(buf, "hello%s%d\n", name_.c_str(), VERSION);
    net_->send_bytes(buf);
  }

 private:
  std::string name_;
  Network* net_;
};

// TODO: Use non-blocking sockets!
int main() {
  std::string address;
  int port;
  std::string name;
  std::cout << "Please, enter your name: ";
  std::cin >> name;
  // temporal solution - we should use broadcast message
  address = "127.0.0.1";
  port = 8888;
  //////////////////////
  Sender sender(name, address.c_str(), port);
  sender.connect();
  sender.greet_server();
  for (;;) {
    sender.send_message("Hello!");
    sender.send_message("My name is Client!");
    std::cout << sender.get_all_from_server(0);
  }
  return 0;
}
